import asyncio
from bleak import BleakScanner, BleakClient
from bleak.backends.characteristic import BleakGATTCharacteristic
from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from typing import List, Tuple
from flask_socketio import SocketIO
import struct
import threading
import zq_data

# 全局变量，用于存储发现的设备
devices_dict = {}

selected_ble_socket = None
ble_recv_data = bytearray()

async def list_ble_devs(timeout: int = 3) -> List[Tuple[str, str, str]]:
    """
    扫描BLE设备并显示结果

    Args:
        timeout: 扫描时长（秒）
    """
    devices_dict = {}
    print(f"开始扫描BLE设备，扫描时长{timeout}秒...")

    def callback(device: BLEDevice, advertisement_data: AdvertisementData):
        if device.name:
            devices_dict[device.address] = {
                "name": device.name,
                "rssi": advertisement_data.rssi
            }
        print(f"发现设备: {device.name or '未知设备'} [{device.address}], RSSI: {advertisement_data.rssi}")

    async with BleakScanner(callback) as scanner:
        await asyncio.sleep(timeout)

    return [(key, device["name"], device["rssi"]) for key, device in devices_dict.items()]

def select_dev(dev:str, socket:SocketIO):
    global selected_dev, selected_ble_socket
    print("用户选择BLE设备:{dev}")
    selected_dev = dev
    selected_ble_socket = socket

def ToggleBleDev(is_running = False) -> None:
    global thread_stop
    if is_running:
        # thread_stop = threading.Event()
        thread_stop.clear()
        log_handle = threading.Thread(target=log_thread, daemon=True)
        log_handle.start()
    else:
        thread_stop.set()

selected_dev = None
# 设备句柄
dev_handle = None
# 线程停止标志
thread_stop = threading.Event()
# 设备交互线程
dev_thread = None
socket = None

def StartDev(dev_name:str):
    global dev_handle, dev_thread, thread_stop, selected_dev
    if dev_handle is not None:
        return dev_handle
    selected_dev = dev_name
    dev_thread = threading.Thread(target=log_thread, daemon=True)
    thread_stop.clear()
    dev_thread.start()

def StopDev() -> None:
    global dev_handle, dev_thread, thread_stop, selected_dev
    thread_stop.set()
    if dev_thread is not None:
        dev_thread.join()
        dev_thread = None
    # if dev_handle is not None:
    #     dev_handle.close()
    #     dev_handle = None


# 定义通知处理函数
recv_buf = bytearray()
def notification_handler(sender: int, data: bytearray) -> None:
    global recv_buf, socket
    # 读取设备数据并扩展buf
    recv_buf.extend(data)
    # 处理接收数据
    recv_buf, log1, log2 = zq_data.recv(recv_buf, "", "")
    if len(log1) > 0:
        socket.emit('log1', {'data': log1})
        log1 = ""
    if len(log2) > 0:
        socket.emit('log2', {'data': log2})
        log2 = ""


#     await client.write_gatt_char(char_uuid, data)
def log_thread() -> None:
    global dev_handle, dev_thread, thread_stop, selected_dev, devices_dict

    if selected_dev in devices_dict:
        device_name = devices_dict[selected_dev]["name"] or "未知设备"
        print(f"正在连接到设备: {device_name} [{selected_dev}]")
    else:
        print(f"正在连接到设备: [{selected_dev}]")

    async def run_ble_client():
        global thread_stop
        try:
            # 连接到BLE设备
            async with BleakClient(selected_dev, timeout=2) as client:
                if not client.is_connected:
                    print("连接失败")
                    return
                print(f"成功连接到设备: {selected_dev}")
                # 查找所有服务和特征
                GattCharWrite = GattCharRead = None
                print("\n设备服务和特征:")
                for service in client.services:
                    short_uuid = service.uuid[4:8].upper()
                    if short_uuid == "FFF0":  
                        print(f"  服务: {short_uuid} [{service.uuid}]")
                        for char in service.characteristics:
                            props = ",".join(char.properties)
                            if props.lower() == "write-without-response":  # 显示特征属性
                                GattCharWrite = char
                            elif props.lower() == "notify":   
                                GattCharRead = char
                            print(f"    特征: {char.uuid} [{props}]")

                # 是否需要切换到高级log模式
                adv_cmd_pack = zq_data.get_log_switch_pack()
                if adv_cmd_pack:
                    await client.write_gatt_char(GattCharWrite, adv_cmd_pack)

                # 启动接收通知
                await client.start_notify(GattCharRead, notification_handler)
                print(f"已启动从特征 {GattCharRead} 的通知接收")
                
                # 保持线程运行，直到收到停止信号
                while not thread_stop.is_set():
                    # 是否有OTA发送数据处理
                    ota_pack = zq_data.get_ota_pack()
                    if ota_pack:
                        for i in range(0, len(ota_pack), 200): # ble pack size
                            await client.write_gatt_char(GattCharWrite, ota_pack[i:i+200])
                    else:
                        await asyncio.sleep(0.05)
                
                # 停止清理
                await client.stop_notify(GattCharRead)
                print("已停止通知接收")
                
        except Exception as e:
            print(f"BLE客户端错误: {e}")
    
    # 在线程内运行异步函数 - 尝试5次
    for i in range(5):
        asyncio.run(run_ble_client())

